package org.tigr.htc.request;

/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

import org.apache.log4j.Logger;
import org.tigr.antware.shared.command.events.CommandRuntimeStatusLI;
import org.tigr.antware.shared.command.events.CommandStatusLI;
import org.tigr.antware.shared.command.events.FailureEvent;
import org.tigr.antware.shared.command.events.FinishEvent;
import org.tigr.antware.shared.command.events.InterruptEvent;
import org.tigr.antware.shared.command.events.ResumeEvent;
import org.tigr.antware.shared.command.events.StartEvent;
import org.tigr.antware.shared.command.events.SubmitEvent;
import org.tigr.antware.shared.command.events.SuspendEvent;
import org.tigr.antware.shared.exceptions.IDGenerationException;
import org.tigr.antware.shared.idgen.IDGenerator;
import org.tigr.antware.shared.util.RunSystemCommand;
import org.tigr.htc.cmd.CmdState;
import org.tigr.htc.cmd.Command;
import org.tigr.htc.cmd.TaskGroup;
import org.tigr.htc.common.HTCConfig;
import org.tigr.htc.server.IJobMonitor;
import org.tigr.htc.server.IRunner;
import org.tigr.htc.server.JobMonitorFactory;
import org.tigr.htc.server.RunnerFactory;
import org.tigr.htc.server.RunnerMonitor;
import org.tigr.htc.server.sge.SgeRunner;

/**
 * The <b>SGEMockServer</b> class is used for testing Sun Grid Engine or for simulating the HTC Service
 * locally. This class implements methods that submit and monitor jobs locally as opposed to going to the
 * HTC web service.
 * <p>
 * This class adheres to the Singleton pattern.
 *
 */

public class SgeMockServer
    implements IProxyServer, CommandRuntimeStatusLI, CommandStatusLI {
    
    private static Logger logger = Logger.getLogger(SgeMockServer.class);
    private static Runtime rt = Runtime.getRuntime();
    
    /**
     * The variable <code>instance</code> holds the single instance of this Singleton 
     * class
     */
    private static SgeMockServer instance = null;
    
    /**
     * The variable <code>requests</code> holds the requests mapped by the assigned
     * HTC IDs. This map is later used to fire events back to the client.
     * 
     */
    private static HashMap<Long, HTCRequest> requests = new HashMap<Long, HTCRequest> ();
    
    /**
     * The variable <code>commands</code> holds the new commands built from the requests
     * These commands have the server registered as a listener so it can translate the
     * Command to the appropriate request
     * 
     */
    private static HashMap<Long, Command> commands = new HashMap<Long, Command> ();
    
    /**
     * <code>ENV_CMD</code> command to get local environment
     * TODO: It possible to dispense with shelling out to the env command. Can't we just
     * use Java's environment support?
     */
    static final String ENV_CMD = "/usr/bin/env";
    
    /**
     * <code>env</code> save environment here
     *
     */
    static HashMap <String, String> env;
    
    
    /**
     * The variable <code>runners</code> holds the runners that match the Command submitted
     */
    private static HashMap<Long, IRunner> runners = new HashMap <Long, IRunner> ();
    private static String WORKING_DIR = "working";
    public static int MASK = 0x3fff;
    
    
    /**
     * The variable <code>local</code> holds the local submission status of the server  
     */
    private static boolean local = true;
    
    /**
     * The variable <code>passEnvironment</code> hold the configuration value for this configuration
     */
    private boolean passEnvironment = true;
    
    /**
     * The constructor <code>SGEMockServer</code> creates a new instance of the mock server. This is
     * a private constructor to adhere to the Singleton pattern. Use the <code>getInstance</code>
     * method to obtain an instance of this class
     * @throws IOException 
     * 
     */
    private SgeMockServer () {
        logger.info("Initializing mock server.");
        
        // Set the working directory if defined in the config file
        if (HTCConfig.getProperty("CWD") != null) {
            WORKING_DIR = HTCConfig.getProperty("CWD");
            logger.info("Current working directory set to: " + WORKING_DIR);
        }
        
        // Get the configuration for passEnvironment from the config file if specified
        // Else set it to true
        if (HTCConfig.getProperty("grid.passenv") != null) {
        	passEnvironment = Boolean.parseBoolean(HTCConfig.getProperty("grid.passenv"));
        	logger.info("The directive for passing environment by default to grid jobs is: " + passEnvironment);
        }
        
        if (passEnvironment) {
			// Grab local the environment in case the user wants the local environment to be passed along
			logger.debug("Grab the local environment");
			Map<String, String> envMap = System.getenv();
			// Iterate over the environment and add to the env vector. While adding stip some of the
			// environment variables known to cause problems such as LS_COLORS, HOST, HOSTTYPE, HOSTNAME
			env = new HashMap<String, String>();
			if (logger.isDebugEnabled()) {
				logger.debug("----- start environment ----");
			}
			for (String envVar : envMap.keySet()) {

				if (!(envVar.equals("LS_COLORS") || envVar.equals("HOST")
						|| envVar.equals("HOSTTYPE") || envVar
						.equals("HOSTNAME"))) {
					// As the variable is not one of the ones that is skipped add to the vector
					env.put(envVar, envMap.get(envVar));

					if (logger.isDebugEnabled()) {
						logger.debug(envVar + " : " + envMap.get(envVar));
					}
				}
			}
			if (logger.isDebugEnabled()) {
				logger.debug("----- end environment ----");
			}
		}
        
		// Spawn a set of monitors that will monitor the jobs
        logger.info("Creating monitor threads.");
        
        // Get the number of monitor threads from the config file
        int monitorThreads = Integer.parseInt(HTCConfig.getProperty("MONITOR_THREAD_COUNT"));
        for(int i=0; i<monitorThreads; i++) {
            RunnerMonitor.spawnMonitor();
        }
        logger.info("Created monitor threads.");
        
        // Set the config file property of the IDGenerator class
        IDGenerator.setConfigFile(System.getProperty("org.tigr.htc.config"));
        if (logger.isDebugEnabled()) {
            logger.debug("Setting ID Generation config file: " + System.getProperty("org.tigr.htc.config"));
        }
    }
    
    
    /**
     * The method <code>getInstance</code> method returns an instance of the
     * MockServer. If the instance does not exist, one is created.
     * 
     * @return a <code>IProxyServer</code> instance
     */
    public static IProxyServer getInstance() {
        synchronized (ENV_CMD) {
			if (instance == null) {
				instance = new SgeMockServer();
			}
		}
		return instance;
    }
    
    /* (non-Javadoc)
     * @see org.tigr.htc.request.IProxyServer#getCommand(long)
     */
    public Command getCommand(long p_htcID, HTCRequest p_request) throws IOException {
        logger.debug("Attempting to rebuild the Command for request: " + p_request);
        
        long cid = p_htcID;
        
        // Recreate the local command for the HTC request
        // TODO: Currently hardcoding the runner used to recreate the command
        // to SGERunner. Will have to re-do this to make MockServer universal
        logger.debug("Command id masks to " + (cid & ~MASK));
        File parentDir = new File(WORKING_DIR, Long.toString(cid & ~MASK));
       
        Command localCommand = SgeRunner.recreateCommand(parentDir.getPath(), cid);
        
        // Create a runner that can be used to query the state of this job
        try {
            logger.debug("Attempting to create runner for Command: " + localCommand.getCommand());
            
            // Build a runner and query the runner for the current state of this job
            // TODO: Currently hard coded for SGE, modify for universality
            SgeRunner runner = (SgeRunner) RunnerFactory.newRunner(localCommand, parentDir);
            runner.createEventReaders(); // Create event readers as this is re-monitoring
            
            // Call the method that is used for monitoring jobs to set the current state
            // of the command being executed by the runner. If the job has completed then
            // return, if not then add the runner to the list of requests that need monitoring
            // and register listeners for the same
            if (runner.isRunnerComplete()) {
                logger.debug("Command is already complete, so returning.");
            }
            else if (localCommand.getState().equals(CmdState.INIT) || 
            			localCommand.getState().equals(CmdState.WAITING)) {
            	// When the runner is not complete, check the state of the Command 
            	// object. If it is either in INIT or WAITING states, set the 
            	// state of command object to INTERRUPTED, so that it gets 
            	// resubmitted during the reconnection process
            	
            	localCommand.setState(CmdState.INTERRUPTED);
            }
            else {
                logger.debug("Command is not complete, so start monitoring it.");
                // If the job is not in finished state then add the runner
                // to the runners that are monitored
                
                // Register this server as the status and lifetime listener. 
                // The mock server receives grid events
                // and translates them as needed.
                localCommand.addRuntimeStatusListener(this);
                localCommand.addStatusListener(this);
                
                // Add the request to the requests hash in a synchronized block to make sure that
                // some other thread does not modify the hash at the same time
                Long CID = new Long(cid);
                synchronized (requests) {
                    logger.debug("Adding runner for Command with HTC id: " + CID);
                    requests.put(CID, p_request);
                    commands.put(CID, localCommand);
                    runners.put(CID, runner);
                }
            }
            
        } catch (Exception e) {
            throw new IOException("Mock server can't get Command " + e.getMessage());
        }
        
        // Clone the local command and return the clone as the listeners registered on the
        // local command are different than the ones in the request
        Command cmd = (Command) localCommand.clone();
        
        if (logger.isDebugEnabled()) {
            logger.debug("Returning Command: " + cmd);
        }
        return cmd;
    }

    /* (non-Javadoc)
     * @see org.tigr.htc.request.IProxyServer#isNFS(java.lang.String)
     */
    public boolean isNFS(String p_dir) throws FileNotFoundException {
        logger.debug("In isNFS");
        File testFile = new File(p_dir);
        Process p;

        for (int i = 0; i < 2; i++) {
            if (testFile.exists()) {
                logger.debug("File exists");
                try {
                    logger.debug("cmd is " + p_dir);
                    p = rt.exec("/bin/df -t nfs " + p_dir);
                    BufferedReader out =
                        new BufferedReader(new InputStreamReader(p.getInputStream()));
                    String line = null;

                    line = out.readLine(); // eat header
                    logger.debug("df out " + line);
                    line = out.readLine();
                    logger.debug("df out " + line);
                    if (line != null) {
                        return true;
                    }

                    int exitVal = p.waitFor();
                    logger.debug("Process exit value: " + exitVal);
                    return false;
                } catch (Exception e) {
                    logger.debug("Exception " + e);
                    return false;
                }
            } else {
                logger.debug("File doesn't exist: " + testFile.getName());
                if ((testFile = testFile.getParentFile()) != null) {
                    p_dir = testFile.getAbsolutePath();
                } else {
                    break;
                }
            }
        }
        throw new FileNotFoundException("Path or File doesn't exist " + p_dir);
    }

    /**
     * The method <code>submit</code> submits the job represented by this request to the
     * underlying grid. This method clones the client command to isolate client command
     * from the underlying grid events.
     * 
     * @param pRequest a <code>HTCRequest</code> object representing the job to execute on the GRID 
     * @throws IOException 
     */
    public synchronized long submit(HTCRequest pRequest) throws IllegalAccessException, IOException {
        // Set the HTC id for this command encompassed by the request
        long cid = 0;
        File parentDir;
        try {
            cid = IDGenerator.generateUniqueID();
            // Create the parent directory of the command directory that will hold the 
            // command files. The parent directory is created by masking off the lower order 
            // bytes of the ID. The applied mask is 0x3fff  
            logger.debug("Starting Command " + cid);
            logger.debug("Command id masks to " + (cid & ~MASK));
            parentDir = new File(WORKING_DIR, Long.toString(cid & ~MASK));
            
            if (!parentDir.exists()) {
                logger.debug("Making parent directory: " + parentDir);
                if (!parentDir.mkdirs()) {
                	if (! parentDir.exists()) {
                        logger
                        .fatal("Couldn't create the directory needed by the server "
                                + parentDir);
                        throw new IOException("Config error, " + parentDir
                                + " could not be created");
                	} else {
                		logger.debug("Some other JVM probably created the parent directory " + parentDir);
                	}
                }
                
                // Change permissions to 777 for the parent directory
                String chmodCmd = "chmod 777 " . concat(parentDir.getAbsolutePath());
                logger.debug("Chmod command " + chmodCmd);

                RunSystemCommand run = new RunSystemCommand();
                try {
					run.runCommand(chmodCmd);
				} catch (Throwable e) {
					logger.debug("Error running cCommand " + chmodCmd + ". " + run.getErrString());
					throw new IllegalAccessException("Could not change permissions of parent directory" +
							parentDir.getAbsolutePath());					
				}                
            }
        }
        catch (IDGenerationException e1) {
            throw new IllegalAccessException("Could not generate unique id." + e1.getMessage());
        }
        
        Command cmd = pRequest.cmd;
        cmd.setID(cid);
        
        // Clone the command and register listeners on the cloned command. This is done to isolate the
        // client Command from receiving events from the underlying grid events. 
        Command localCommand = (Command) cmd.clone();
        
        // Set the environment to the Command that is being executed based on the user settings or global
        // configuration. If the user has specified that the environment should be set in the request then
        // honor that, else use the global configuration file settings.
        if (pRequest.isGetEnv()) {
        	if (logger.isDebugEnabled()) {
        		logger.debug("Get environment property set to true on command. Will pass environemt.");
        	}
        	
        	// Set the user specified environment
            if (pRequest.getEnv() != null) {
            	if (logger.isDebugEnabled()) {
					logger.debug("Setting user specified environment");
				}
                localCommand.setEnv((Map<String, String>) pRequest.getEnv());
            } else {
            	// Set the default environment that was grabbed by the 
            	// SGEMockServer during the initialization as the user has not specified
            	// the environment
            	if (logger.isDebugEnabled()) {
					logger.debug("Setting local environment");
				}
            	localCommand.setEnv(SgeMockServer.env);
            }
        }
        else if (passEnvironment) {
        	// As the global configuration specifies passing an environment pass the local
        	// environment
        	if (logger.isDebugEnabled()) {
				logger.debug("Setting local environment");
			}
        	localCommand.setEnv(SgeMockServer.env);
        }
        
        // Register this server as the status and lifetime listener. The mock server receives grid events
        // and translates them to appropriate client events as needed.
        localCommand.addRuntimeStatusListener(this);
        localCommand.addStatusListener(this);
        
        // Add the request to the requests hash in a synchronized block to make sure that
        // some other thread does not modify the hash at the same time
        Long CID = new Long(cid);
        synchronized (requests) {
            requests.put(CID, pRequest);
            commands.put(CID, localCommand);
        }
        try {
            logger.debug("Attempting to create runner for Command: " 
                    + localCommand.getCommand());
            
            // Build a runner and create a thread that will launch this runner
            IRunner runner = RunnerFactory.newRunner(localCommand, parentDir);
            Thread t = new Thread(runner);
            if (t != null) {
                t.setName("HTC id: " + cid + " runner");
                t.start();
            }
            else {
                throw new IllegalAccessException("Could not create thread to " +
                        "launch runner.");
            }
            
            // Add the runner to the hash
            synchronized (runners) {
                logger.debug("Adding runner for Command with HTC id: " + CID);
                runners.put(CID, runner);
            }
        } catch (Exception e) {
            throw new IllegalAccessException(e.getMessage());
        }
        return cid;
    }

    /* (non-Javadoc)
     * @see org.tigr.htc.request.IProxyServer#getStatus(org.tigr.htc.request.HTCRequest)
     */
    public Map getStatus(HTCRequest p_request) {
        return (Map) new HashMap();
    }

    
    /* (non-Javadoc)
     * @see org.tigr.htc.request.IProxyServer#getMonitorHost(org.tigr.htc.request.HTCRequest)
     */
    public String getMonitorHost(HTCRequest p_request) throws IOException {
        try {
            return InetAddress.getLocalHost().getHostName();
        }
        catch (UnknownHostException e) {
            throw new IOException("Mock server could not release job: " + p_request.cmd);
        }
    }

	public TaskGroup getTasks(HTCRequest p_request) throws IOException {
		throw new IOException("mock server doesn't support action");
	}
	
    /**
     * The method <code>resume</code> releases a hold placed on the current request from 
     * the queue
     * 
     * @param a <code>HTCRequest</code> object representing the request to be resumed
     * in the queue.
     */
    public void resume(HTCRequest pRequest) throws IOException {
        // First obtain the monitor and invoke the method on the monitor
        // and the invoke the method to stop the request
        try {
            synchronized (requests) {
                Long CID = new Long(pRequest.cmd.getID());
                Command cmd = (Command) commands.get(CID);

                IJobMonitor monitor = JobMonitorFactory.getMonitor(cmd);
                if (monitor.resume(cmd)) {
                    // Fire task resumed
                }
            }
        } catch (Exception e) {
            throw new IOException("Mock server could not release job: " + pRequest.cmd);
        }
    }

    /**
     * The method <code>stop</code> removes the current request from the queue
     * 
     * @param a <code>HTCRequest</code> object representing the request to be removed
     * from the queue.
     */
    public void stop(HTCRequest request) throws IOException {
        logger.debug("Requesting a stop on request: " + request);
        // First obtain the monitor and invoke the method on the monitor
        // and the invoke the method to stop the request
        synchronized (requests) {
            Long CID = new Long(request.cmd.getID());
            Command cmd = (Command) commands.get(CID);
            IRunner runner = null;
            runner = runners.get(CID);
            if (runner != null) {
                try {
                    runner.stop();
                }
                catch (Exception e) {
                    IOException e1 = new IOException("Could not stop job: " + cmd.getID()
                            + ". Erros is " + e.getMessage());
                    e1.setStackTrace(e.getStackTrace());
                    throw e1;
                }
            }
            else {
                logger.warn("Could not find runner for request: " + CID);
            }
        }
    }

    /**
     * The method <code>hold</code> holds the current request from the queue
     * 
     * @param a <code>HTCRequest</code> object representing the request to be paused for
     * execution in the queue.
     */
    public void hold(HTCRequest pRrequest) throws IOException {
        // First obtain the monitor and invoke the method on the monitor
        // and the invoke the method to stop the request
        try {
            synchronized (requests) {
                Long CID = new Long(pRrequest.cmd.getID());
                Command cmd = (Command) commands.get(CID);

                IJobMonitor monitor = JobMonitorFactory.getMonitor(cmd);
                if (monitor.hold(cmd)) {
                    // Fire task held
                }
            }

        } catch (Exception e) {
            throw new IOException("Mock server could not hold job: " + pRrequest.cmd);
        }
    }

    public void disconnect(HTCRequest pRequest) throws IOException {
        throw new IOException("Mock server doesn't support action");
    }

    /**
     * run starting point
     *
     */
    public void run() {
        try {
            // create events
        } catch (Exception e) {
            logger.fatal("main -- " + e.getMessage(), e);
        }

    }


    /* (non-Javadoc)
     * @see org.tigr.antware.shared.command.events.CommandLifetimeLI#commandStarted(org.tigr.antware.shared.command.events.StartEvent)
     */
    public void commandStarted(StartEvent pEvent) {
        // Clone the start event and change the source to be the request as opposed to 
        // the command
        Command cmd = (Command) pEvent.getSource();
        
        logger.debug("Received start event from command with ID: " + cmd.getID() 
        		+ " and grid id " + cmd.getGridJobID());
        logger.debug("The source type is " + pEvent.getSource().getClass()); 
        HTCRequest request = (HTCRequest) requests.get(new Long(cmd.getID()));
        request.cmd.setGridJobID(cmd.getGridJobID()); 
        
        StartEvent startEvent = new StartEvent(request, pEvent.getCommandID(),
                pEvent.getDate(), pEvent.getMessage(), pEvent.getMachine());
        request.cmd.fireStartEvent(startEvent);
        logger.debug("Finished processing start event.");
    }


    /* (non-Javadoc)
     * @see org.tigr.antware.shared.command.events.CommandLifetimeLI#commandFinished(org.tigr.antware.shared.command.events.FinishEvent)
     */
    public void commandFinished(FinishEvent pEvent) {
        // Clone the finish event and change the source to be the request as opposed to 
        // the command
        Command cmd = (Command) pEvent.getSource();
        logger.debug("Received finish event from Command with id " + cmd.getID()
        		+ " and grid id " + cmd.getGridJobID());
        Long CID =  new Long(cmd.getID());
        
        HTCRequest request = requests.get(CID);
        request.cmd.setGridJobID(cmd.getGridJobID()); 
        FinishEvent finishEvent = new FinishEvent(request, pEvent.getCommandID(),
                pEvent.getDate(), pEvent.getMessage(), pEvent.getReturnValue());
        request.cmd.fireFinishEvent(finishEvent);   
        
        // Remove the request after the job finishes
        synchronized (requests) {
            requests.remove(CID);
            commands.remove(CID);
        } 
        synchronized (runners) {
            runners.remove(CID);
        }
        logger.debug("Finished processing finish event.");      
    }


    /* (non-Javadoc)
     * @see org.tigr.antware.shared.command.events.CommandRuntimeStatusLI#commandSubmitted(org.tigr.antware.shared.command.events.SubmitEvent)
     */
    public void commandSubmitted(SubmitEvent pEvent) { 
        // Clone the submit event and change the source to be the request as opposed to 
        // the command
        Command cmd = (Command) pEvent.getSource();
        
        logger.debug("The source type is " + pEvent.getSource().getClass());
        logger.debug("Received submit event from Command with id: " + cmd.getID()
        		+ " and grid id " + cmd.getGridJobID());
        HTCRequest request = (HTCRequest) requests.get(new Long(cmd.getID()));
        request.cmd.setGridJobID(cmd.getGridJobID()); 
        SubmitEvent submitEvent = new SubmitEvent(request, pEvent.getCommandID(),
                pEvent.getDate(), pEvent.getMessage(), pEvent.getMachine(), pEvent.getUser(),
                pEvent.getLogLocation());
        request.cmd.fireSubmitEvent(submitEvent);
        logger.debug("Finished processing submit event.");
    }


    /* (non-Javadoc)
     * @see org.tigr.antware.shared.command.events.CommandStatusLI#commandRestarted(org.tigr.antware.shared.command.events.StartEvent)
     */
    public void commandRestarted(StartEvent pEvent) {
        // Clone the start event and change the source to be the request as opposed to 
        // the command
        Command cmd = (Command) pEvent.getSource();
        logger.debug("Received restart event from Command with id " + cmd.getID()
        		+ " and grid id " + cmd.getGridJobID());
        HTCRequest request = (HTCRequest) requests.get(new Long(cmd.getID()));
        request.cmd.setGridJobID(cmd.getGridJobID()); 
        StartEvent startEvent = new StartEvent(request, pEvent.getCommandID(),
                pEvent.getDate(), pEvent.getMessage(), pEvent.getMachine());
        request.cmd.fireRestartEvent(startEvent);
        logger.debug("Finished processing restart event.");
    }


    /* (non-Javadoc)
     * @see org.tigr.antware.shared.command.events.CommandStatusLI#commandInterrupted(org.tigr.antware.shared.command.events.InterruptEvent)
     */
    public void commandInterrupted(InterruptEvent pEvent) {
        // Clone the start event and change the source to be the request as opposed to 
        // the command
        Command cmd = (Command) pEvent.getSource();
        logger.debug("Received interrupt event from Command with id " + cmd.getID()
        		+ " and grid id " + cmd.getGridJobID());
        Long CID =  new Long(cmd.getID());
        HTCRequest request = (HTCRequest) requests.get(CID);
        
        if (cmd.getGridJobID() != null) {
        	request.cmd.setGridJobID(cmd.getGridJobID());
        }
        
        InterruptEvent interruptEvent = new InterruptEvent(request, pEvent.getCommandID(),
                pEvent.getDate(), pEvent.getMessage(), pEvent.getInterrupt());
        request.cmd.fireInterruptEvent(interruptEvent);   
        
        // Remove the request after the job is interrupted
        synchronized (requests) {
            requests.remove(CID);
            commands.remove(CID);
        }
        synchronized (runners) {
            runners.remove(CID);
        }
        logger.debug("Finished processing interrupt event.");
    }


    /* (non-Javadoc)
     * @see org.tigr.antware.shared.command.events.CommandStatusLI#commandFailed(org.tigr.antware.shared.command.events.FailureEvent)
     */
    public void commandFailed(FailureEvent pEvent) {
        // Clone the start event and change the source to be the request as opposed to 
        // the Command
        Command cmd = (Command) pEvent.getSource();   
        logger.debug("Received failure event from Command with id: " + cmd.getID());     
        Long CID =  new Long(cmd.getID());
        HTCRequest request = (HTCRequest) requests.get(CID);
        request.cmd.setGridJobID(cmd.getGridJobID()); 
        FailureEvent failureEvent = new FailureEvent(request, pEvent.getCommandID(),
                pEvent.getDate(), pEvent.getMessage(), pEvent.getFailure());
        request.cmd.fireFailureEvent(failureEvent);    
        
        // Remove the request after the job fails
        synchronized (requests) {
            requests.remove(CID);
            commands.remove(CID);
        } 
        synchronized (runners) {
            runners.remove(CID);
        }
        logger.debug("Finished processing failure event.");     
    }


    /* (non-Javadoc)
     * @see org.tigr.antware.shared.command.events.CommandRuntimeStatusLI#commandResumed(org.tigr.antware.shared.command.events.ResumeEvent)
     */
    public void commandResumed(ResumeEvent pEvent) {        
        // Clone the start event and change the source to be the request as opposed to 
        // the command
        Command cmd = (Command) pEvent.getSource();   
        logger.debug("Received resume event from Command with id: " + cmd.getID());     
        Long CID =  new Long(cmd.getID());
        HTCRequest request = (HTCRequest) requests.get(CID);
        request.cmd.setGridJobID(cmd.getGridJobID());         
        ResumeEvent resumeEvent = new ResumeEvent(request, pEvent.getCommandID(),
                pEvent.getDate(), pEvent.getMessage(), pEvent.getMachine());
        request.cmd.fireResumeEvent(resumeEvent); 
        logger.debug("Finished processing resume event.");         
    }


    /* (non-Javadoc)
     * @see org.tigr.antware.shared.command.events.CommandRuntimeStatusLI#commandSuspended(org.tigr.antware.shared.command.events.SuspendEvent)
     */
    public void commandSuspended(SuspendEvent pEvent) {        
        // Clone the start event and change the source to be the request as opposed to 
        // the command
        Command cmd = (Command) pEvent.getSource();   
        logger.debug("Received suspend event from Command with id: " + cmd.getID());     
        Long CID =  new Long(cmd.getID());
        HTCRequest request = (HTCRequest) requests.get(CID);
        request.cmd.setGridJobID(cmd.getGridJobID()); 
        SuspendEvent suspendEvent = new SuspendEvent(request, pEvent.getCommandID(),
                pEvent.getDate(), pEvent.getMessage(), pEvent.getSuspend());
        request.cmd.fireSuspendEvent(suspendEvent);
        logger.debug("Finished processing suspend event.");         
    }
    
    @Override
    public Object clone() throws CloneNotSupportedException {
    	throw new CloneNotSupportedException();
    }

    /**
     * The method <code>isLocal</code> returns the status of the proxy server. For local submissions this 
     * value is true and for remote submissions the value is false.
     *  
     * @return boolean true to indicate the server is performing local submits
     */
	public boolean isLocal() {
		return local;
	}

    /**
     * The method <code>isPassEnvironment</code> provides the configuration for the proxy server
     * to pass the local environment to the grid jobs. This is read from a global configuration file.
     * 
     * @return a <code>boolean</code> of true if the environment is explicitly passed to the grid jobs
     * when the environment is not set by the user.
     */
	public boolean isPassEnvironment() {
		return passEnvironment;
	}
}
